package com.example.demo.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * RetryUtils
 *
 * @since 2022/8/4 16:29
 */
public class RetryUtils {
	
	/**
     * 日志.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(RetryUtils.class);
    
    private static final Integer DEFALUT_RETRYTIMES = 3;

    private static final Integer DEFAULT_AWAIT_SECONDS = 1;


    /**
     * retry
     *
     * @param supplier     重试操作
     * @param retryTimes   重试次数 例传参为2  则加上重试2次 一共执行3次
     * @param awaitSeconds 每次重试等待时间
     * @param exception    每次捕获指定异常后会重试 否则直接结束 注意这里是子异常 例如SqlLiteException.class 则只会捕获SqlLiteException
     * @return 返回操作自身返回的结果
     * @since 2022/8/7 13:53
     */
    public static <T> T retry(RetryFunction<T> supplier, int retryTimes, int awaitSeconds, Class<? extends Exception> exception) throws Exception {
        int times = 0;
        T result = null;
        while (retryTimes-- >= 0) {
            if (times > 0) {
            	LOGGER.info("RetryUtils retry times:{} start...", times);
            }

            try {
                result = supplier.get();
                break;
            } catch (Exception e) {
                if (times > 0) {
                	LOGGER.warn("RetryUtils retry times:{} end...", times, e);
                }
                if (e.getClass() != exception) {
                    throw e;
                } else if (retryTimes >= 0) {
                    try {
                        Thread.sleep(1000L * awaitSeconds);
                    } catch (InterruptedException ex) {
                    	LOGGER.warn("thread interrupted");
                    }
                }
            }

            times++;
        }
        return result;
    }

    public static <T> T retry(RetryFunction<T> supplier) throws Exception {
        return retry(supplier, DEFALUT_RETRYTIMES, DEFAULT_AWAIT_SECONDS, Exception.class);
    }

    @FunctionalInterface
    public interface RetryFunction<T> {

        /**
         * Gets a result.
         *
         * @return a result
         */
        T get() throws Exception;
    }
}
